Fork me on GitHub Android 《第一行代码》简要笔记(一) - 冰路梦 | binglumeng

Android 《第一行代码》简要笔记(一)

Posted by 冰路梦 on 2017-06-06

Android 《第一行代码》简要笔记(一)

第一章,开始启程

Android系统,2003年10月,Andy Rubin创办Android公司。2005年8月,Google收购Android。

  • 系统架构

    Android系统基于Linux内核,分为:Linux内核层、系统运行库层、应用框架层、应用层。

  • 版本信息

    2008年9月,Android 1.0版本发布。之后2.1、2.2、2.3。

    2011年2月,3.0版本,专为平板设计;

    2011年10月,Android 4.0版本,统一移动端平台版本。

    2014年Google I/O大会,发布Android 5.0,属于较大版本更新。ART替换Dalvik,Material Design以及Android Wear、TV、Auto等。

    2015年Google I/O大会,Android 6.0,加入运行时权限。

    2016年Google I/O大会,Android 7.0,加入多窗口模式。

    2017年Google I/O大会,Android 8.0,通知渠道、画中画、自适应图标等新特性。

  • Android应用开发特色

    四大组件:Activity、Service、Broadcast Receiver、Content Provider。

    Intent可用于组件间的信息传递。

  • 开发环境

    • JDK
    • Android SDK
    • Android Studio (也可用Eclipse等其他开发工具,但是官方推荐Android Studio,且确实是最优的选择)

第二章、Activity

  • onCreate()方法,在api>=21时候,出现了两个onCreate方法。

    1
    2
    3
    4
    5
    6
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.second_layout);
    Log.d(TAG, "onCreate: @Nullable Bundle");
    }

    新版的重载的onCreate方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 注意,这个onCreate方法,包含两个参数,在api>=21以上才会调用,用于在手机关机等特殊情况下保存Activity的数据,到outPersistentState中
    * 若是Android 5.0以下的手机,则仅仅有这个方法的话,会不显示Layout布局,所以必须用上面的那个onCreate方法。
    * 这两个onCreate也就对应两个onSaveInstanceSate方法。
    *
    * @param savedInstanceState
    * @param persistentState
    */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
    super.onCreate(savedInstanceState, persistentState);
    setContentView(R.layout.second_layout);
    Log.d(TAG, "onCreate: @Nullable PersistableBundle");
    }
  • Intent包含显式意图和隐式意图,在使用隐式意图启动的时候,注意同时匹配actioncategory两项。

    • 1
      2
      3
      4
      //拨打电话,记得在AndroidManifest.xml中申请相应权限
      Intent intent = new Intent(Intent.ACTION_CALL);
      intent.setData(Uri.parse("tel:10086"));
      startActivity(intent);
    • 1
      2
      3
      Intent intent = new Intent(Intent.ACTION_VIEW);
      intent.setData(Uri.parse("https://www.google.com"));
      startActivity(intent);

    自定义个Browser响应Action_view的请求

    1
    2
    3
    4
    5
    6
    //只需要将某个Activity的注册intent-filter修改如下
    <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="http"/>
    </intent-filter>

    在startActivity的时候,更严谨的做法是,使用PackageManager.queryIntentActivity来查看时候有可以响应inetent的程序,在具体处理。

  • Intent传递数据

    intent.putExtra,然后有接收方使用intent.getExtra获取数据。Activity之间的数据传递,startActivityForResultsetResult,然后在onActivityResult中接收返回的数据。

1、Activity的生命周期

应用程序App中的Activity一般都是处于中,属于LIFO模式,即后进先出。

Activity有四种存在状态:活动、暂停、停止、销毁。

  • onCreate(),创建
  • onStart(),启动
  • onResume(),进入前台模式
  • onPause(),退出前台模式,可能是一个Activity被销毁前最后调用到的方法。(这七个方法中)
  • onStop(),停止,并非一定会调用到,比如强制销毁程序等非正常操作。
  • onDestroy(),销毁
  • onRestart(),若在onDestroy之前再次返回这个Activity,就会调用本方法。

Activity的生命周期,分为三种模式:

  1. 完整生命周期,即从onCreateonDestroy这六个方法包含的全过程。
  2. 可见生命周期,从onStartonStop之间的生存阶段。
  3. 前台生命周期,在onResumeonPause之间的生存期。

例如:Activity 启动一个Dialog,则其本身只是调用了onPause,不会执行到onStop

Home键,onPause-->onStop

Back键,onPause-->onStop-->onDestroy

程序避免退出前台而数据丢失,可使用onSaveInstanceState方法保存数据,在onCreate中判断非空来取出。Android 5.0之后的版本,这两个方法都多了一个重载。

2、Activity启动模式

standardsingleTopsingleTasksingleInstance四种启动模式

  • standard,标准的,每次调用启动,都是新建一个置于栈顶。

  • singleTop,只有该Activity处于栈顶时,不需要新建,若非栈顶,新建置于栈顶。

  • singleTask,本app返回栈内无此Activity则新建,若有,不论是否栈顶,都会清理掉activity之上的所有activity,使之处于栈顶。

  • singleInstance,该Activiy则会独立占用一个新的返回栈,类似与singleTask+taskAffinity

    App中,Activity界面,A(standard)—>B (singleInstance)—>C(standard),此时按下返回键,

    C—>A,再按返回,APP销毁,显示B程序,再返回便会销毁B程序。

如何知晓当前是哪个Activity在栈顶,使用BaseActivity,其他Activity继承它,使用getClass().getSimpleName()可知当前Activity

随时退出整个App的所有Activity,使用ActivityCollector类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> public class ActivityCollector {
> public static List<Activity> activities = new ArrayList<>();//Activity集合
> //加入集合中
> public static void addActivity(Activity activity){
> activities.add(activity);
> }
> //从集合中移除
> public static void removeActivity(Activity activity){
> activities.remove(activity);
> }
> //关闭所有
> public static void finishAll(){
> for(Activity activity : activities){
> activity.finish();
> }
> }
> }
>

>

然后在BaseActivity中添加,移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> public class BaseActivity extends AppCompatActivity {
> @override
> protected void onCreate(Bundle savedInstanceState){
> super.onCreate(savedInstanceState);
> Log.d("BaseActivity",getClass().getSimpleName());
> //将子类的Activity加入到集合中
> ActivityCollector.addActivity(this);
> }
> //子类销毁时候,就从集合中移除
> @override
> protected void onDestroy(){
> super.onDestroy();
> ActivityCollector.removeActivity(this);
> }
> }
>

>

在其他任意地方调用ActivityCollector.finishAll()就能结束所有Activity。

让其他Activity调用本Activity的一个写法:

1
2
3
4
5
6
public static void actionStart(Context context,String data1,String data2){
Intent intent = new Intent(context,this);
intent.putExtra("data1",data1);
intent.putExtra("data2",data2);
context.startActivity(intent);
}

第三章、UI

1、View

View是Android中所有的控件的基类,包括ViewGroup也是继承自View。

  1. Button

    textAllCaps="false"可以在xml中将Button显示的Text文本禁用全部大写。

  2. ProgressBar

    style="?android:attr/progressBarStyleHorizontal"使用来引用Android系统的一些属性

  3. AlertDialog

    dialog.setCancelable(false);//使在Dialog外部点击,不会消失。另有ProgressDialog

2、ViewGroup

继承自View,以前说常用的有五种:AbsoluteLayout(早已经废弃)、RelativeLayoutLinearLayoutTableLayout(少用)、FrameLayout

新的说法:线性、帧布局、相对布局、百分比布局。

==另:==Android N,伴随AndroidStudio 2.2出现了一个ConstraintLayout约束布局,据说非常强哦!

线性布局、相对布局、帧布局,需要注意的也就是Layout_gravitygravity区别,分别是对外和对内。

layout_weightweight_sum权重。

表格布局中,注意TableRow中最大列数决定整个表格的列数。

  • PercentLayout

    百分比布局,仅仅对线性、相对布局做了扩展,在Android Studio中添加依赖 compile 'com.android.support:percent:24.2.1'

    • PercentLinearLayout
    • PercentFrameLayout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <android.support.percent.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 百分比布局中,可以不用有layout_width和layout_height,-->
    <Button
    android:text="Button Text"
    android:layout_gravity="right|bottom"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%"
    </android.support.percent.PercentFrameLayout>
  • \

    标签,用于引用共享布局。

  • ActionBar

    其实已经不推荐使用ActionBar,而是更好的TitleBar。都是用getActionBargetSupportActionBar获取。

自定义View或者ViewGroup可以继承自相应的View或者ViewGroup来复写对应的方法,构造函数,onMesureonLayoutonDraw等。

ListView强大的列表控件,配合Adapter,普通列表常用ArrayAdapter。现更推荐使用RecyclerView替代。

同百分比布局类似,RecycleView也需要单独添加依赖compile 'com.android.support:recyclerview-v7:24.2.1'

使用RecyclerView需要继承对应的适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
static class ViewHolder extends RecyclerView.ViewHolder {
....
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,flase);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder ,int position){
....
}
@Override
public int getItemCount(){
....
}
}

在使用RecyclerView

1
2
3
4
5
6
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rc);
LinearLayoutManager manager = new LinearLayoutManager(this);
//RelativeLayoutManager,,StaggeredGridLayoutManager(瀑布流动布局);
manager.setOrientation(...)
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);

RecyclerView摒弃了ListView的item点击事件,避免事件冲突,所以在RecyclerView中可以对单个的view控件设置点击事件。

.9图片,用于处理图片在不同屏幕分辨下的正常显示。

第四章、Fragment

Fragment的引入使得Android应用可以更为广泛的适配显示屏幕。Fragment的使用,可以在xml中使用fragment标签控件,也可以在java代码中动态增删替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 静态的使用fragment标签 -->
...
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.fragmenttest.LeftFragment"
android:id="@+id/fg_left"/>
...
<!-- 动态在代码中替换操作fragment,则需要一个容器来容纳fragment-->
...
<FrameLayout
android:id="@+id/fl_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
...

使用代码动态操作Fragment

1
2
3
4
5
6
7
8
9
//调用本方法,传入需fragment对象,
private void replaceFragment(Fragment fragment){
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fl_fragment,fragment);
//添加返回栈,那么按下Back键可以返回到上一步,而非直接关闭本fragment所在Activity
transaction.addToBackStack(null);//参数是描述状态,可以为null
transaction.commit();//使用事务机制,确保操作完整。
}

Fragment与Activity通讯:已知Fragment嵌入在Activity中,两者之间关系紧密却也并非通讯容易。

  • Activity–>Fragment

    1
    2
    3
    //通过FragmentManager来管理fragment,获取相应的对象,
    LeftFragment leftFragment = getSupportFragmentManager().findFragmentById(R.id.fg_left);
    ...
  • Fragment–>Activity

    1
    2
    3
    //在Fragment中有个getActivity方法,获得其绑定的Activity对象
    MainActivity activity = (MainActivity)getActivity();
    ...

同理,Fragment之间的通讯,可以借助它们共有的Activity来作为中介,沟通消息。

1、Fragment生命周期

类似与Activity,Fragment的生命周期同样有运行、暂停、停止和销毁四个状态。

onAttach()–>onCreateView()–>onActivityCreated()–>onDestroyView()–>onDetach()

为适配不同的移动终端,Android应用需要根据设备显示对应的布局。这里用到了限定符常用的根据屏幕尺寸、分辨率、方向作为限制符。

尺寸:smallnormallargexlarge

分辨率:ldpimdpihdpixhdpixxhdpi

方向:landport

最小宽度限定符:类似layout_sw600dp表示这个布局使用于宽度600dp以上的设备显示。

第五章、广播接收者 Broadcast Receiver

Android中广播的消息机制,分为标准广播有序广播。前者面向所有注册接收者,后者根据优先级逐级分发消息,可以在其中中断消息的传播。

广播继承Broadcast Receiver,通过Action过滤接收指定的消息。而LocalBroadcast用于App内部广播,不会被其他App接收到。

广播的注册分为静态注册动态注册

静态注册就是在AndroidManifest中注册receiver标签。这样可以不启动app就能激活广播。

动态注册,实在代码中注册,需要动态注销,避免资源浪费。

注:onReceive()函数内,不要有耗时操作,避免报错。且其中无法开启线程。

  • 接收广播
  1. 继承BroadcastReceiver
  2. 注册广播
    • 静态注册,在AndroidManifest中注册
    • 动态注册,在代码中,创建receiver对象,并添加intent-filter过滤action
    • registerReceiver();
  3. 注销广播(动态注册时,才由此步骤,在适当的方法中注销。一般是onStop(),onDestroy()
  • 发送广播

使用Intent,添加Action,然后sendBroadcast(intent)

发送有序广播,sendOrderedBroadcast(intent,null)

然后receiver根据优先级,逐级接收到消息,可使用abortBroadcast()中断向下传输。

本地广播,使用LocalBroadcastManager获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
private LocalReceiver localReceiver;
private LocalBroadcastReceiver localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
//发送本地广播
Intent intent = new Intent("com.example.broadcasttest.LOCALBROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
...
}

其注销,也是用LocalBroadcastManager。在课本示例中有个强制下线功能的演示,通过广播,结合之前的BaseActivity管理所有Activity的方式,关闭app。其中需要注意的是,注册和注销广播分别在BaseActivityonResumeonPause中,而非onCreate,onStartonStop,onDestroy中,为的是只让处于前台的Activity响应消息即可,不用每个Activity都去响应。

》》Git 使用命令:

1
2
3
4
git clone repository_url;
git init;
git add/add./add folder/add -A
git commit -m "commit msg"